//MIDI Interpreter.cpp
#include "MIDIObj.h"
#include <iostream>
#include <math.h>

MIDIObj::MIDIObj(unsigned char* buffer, int len)
{
	unsigned char commandNum;
	int buffDepth;
	int i;
	int tempDeltaTime;
	unsigned long varLen;
	MIDICommandHandle* midiCommand;
	ArrayBuilder* arrayHead;
	ArrayBuilder* arrayCurr;
	ArrayBuilder* arrayPrev;
	int arrayDepth;

	buffDepth = 0;
	arrayDepth = 0;
	tempDeltaTime = 0;
	arrayHead = 0;

	/*for(i = 0; i < 8; i++)
	{	
		for(int j = 0; j < 12; j++)
		{
			printf("%X ", buffer[i * 12 + j]);
		}
		printf("\n");
	}*/


	while(buffDepth < len)
	{

		/* First thing we have to do is read the DeltaTime for the next command.
		 * Look up Delta Time notation, it's confusing (only use 7 of the 8 bits).
		 */
		tempDeltaTime = 0;

		do
		{
			tempDeltaTime = (tempDeltaTime << 7) + (buffer[buffDepth] & 0x7F);
		}
		while(buffer[buffDepth++] & 0x80);
		
		
		/* MIDI Commands begin with a status byte, which
		 * is determined by checking the 15th bit of the
		 * character (i.e.  0x80 is the check for status bit).
		 *
		 * If the bit is NOT on for a given command, it is
		 * a 'running command', where the midi device
		 * assumes the previous command and enters the new data.
		 */

		if(buffer[buffDepth] & 0x80)
			commandNum = buffer[buffDepth++];

		
		/* If for some reason, the end of the buffer is reached here
		 * we want a clean exit from the constructor.
		 */

		if(buffDepth >= len) 
			break;


		if(commandNum >> 4 != 0x0F)
		{
			midiCommand = (MIDICommandHandle*) malloc(sizeof(MIDICommandHandle));

			midiCommand->command = (commandNum & 0x70) >> 4;
			midiCommand->channel = commandNum & 0x0F;
			midiCommand->deltaTime = tempDeltaTime;

			/* Now that we have the command interpreted, we
			 * use a switch statement to determine what to do.
			 */

			switch(midiCommand->command)
			{
				//2 Byte Commands
				case NOTE_OFF:					
				case NOTE_ON:	
				case AFTERTOUCH:	
				case CONTROL_CHANGE:	

					midiCommand->buff[1] = buffer[buffDepth++];
					midiCommand->buff[0] = buffer[buffDepth++];

					//printf("%X %X %X\n", commandNum, midiCommand->ARGS.buff[1], midiCommand->ARGS.buff[0]);

					break;

				//1 Byte Commands
				case PROGRAM_CHANGE:	
				case CHANNEL_PRESSURE:
				case PITCH_WHEEL:

					midiCommand->buff[1] = buffer[buffDepth++];
					midiCommand->buff[0] = 0;

					//printf("%X %X\n", commandNum, midiCommand->ARGS.buff[1]);
			}

			if(arrayDepth == 0)
			{
				arrayHead = (ArrayBuilder*) malloc(sizeof(ArrayBuilder));
				arrayCurr = arrayHead;
			}
			else
			{
				arrayCurr->next = (ArrayBuilder*) malloc(sizeof(ArrayBuilder));
				arrayCurr = arrayCurr->next;
			}

			arrayDepth++;
			arrayCurr->commHandle = midiCommand;
			arrayCurr->next = 0;
		}
		else
		{
			/* Deal with all the "Special Cases" (0xF?), some of which can
			 * have variable lengths.
			 */

			switch(commandNum)
			{
			case 0xF0:
				// SYSEX Message 
				while(buffer[buffDepth] != 0xF7) buffDepth++;
				break;

			case 0xF1:
				// MTC Quarter Frame Message (F1 xx)
				buffDepth++;
				break;

			case 0xF2:
				// Song Position Pointer (F2 xx xx)
				buffDepth += 2;
				break;

			case 0xF3:
				// Song Select (F3 xx)
				buffDepth++;
				break;

			case 0xF6:
				// Tune Request (F6)
				break;

			case 0xF8:
				// MIDI Clock (F8)
				break;

			case 0xF9:
				// Tick (F9)
				break;

			case 0xFA:
				// MIDI Start (FA)
				break;

			case 0xFB:
				// MIDI Continue (FB)
				break;

			case 0xFC:
				// MIDI Stop (FC)
				break;

			case 0xFE:
				// Active Sense (FE)
				break;

			case 0xFF:
				/* Reset, the crazy state.  Changes functionality
				 * based on the next byte in the sequence.
				 */

				switch(buffer[buffDepth])
				{
				case 0x01:
					// Text (FF 01 len(variable) text)
				case 0x02:
					// Copyright (FF 02 len(variable) text)
				case 0x03:
					// Sequence/Track Name (FF 03 len(variable) text)
				case 0x04:
					// Instrument (FF 04 len(variable) text)
				case 0x05:
					// Lyric (FF 05 len(variable) text)
				case 0x06:
					// Marker (FF 06 len(variable) text)
				case 0x07:
					// Cue Point (FF 06 len(variable) text)
				case 0x08: 
					// Program Name (FF 06 len(variable) text)
				case 0x09:
					// Device(Port) Name (FF 06 len(variable) text)
				case 0x7F:
					// Proprietary Event (FF 7F len(variable) data)

					varLen = 0;
					i = 0;
					
					do
					{
						varLen = (varLen << 7) + (buffer[buffDepth] & 0x7F);
					}while(buffer[buffDepth++] & 0x80);

					buffDepth += varLen;
					
					break;

				case 0x2F:
					// End Of Track (FF 2F 00)

					buffDepth += 2;
					
					break;

				case 0x51:
					// Tempo (FF 51 03 tt tt tt)

					buffDepth += 5;

					break;

				case 0x54:
					// SMPTE Offset (FF 54 05 hr mn se fr ff)

					buffDepth += 7;

					break;

				case 0x58:
					// Time Signature (FF 58 04 nn dd cc bb)

					buffDepth += 6;

					break;

				case 0x59:
					// Key Signature (FF 59 02 sf mi)

					buffDepth += 4;

					break;
				}
			}

		}
		
	} 


	/* Here we have a linked list of Commands, now to make
	 * it an array for easy accessing.  Also, we will need to 
	 * de-allocate the ArrayBuilders.
	 */

	unsigned char d;
	numCommands = arrayDepth;
	commandHead = (MIDICommandHandle*) malloc(sizeof(MIDICommandHandle) * arrayDepth);
	arrayCurr = arrayHead;
	for(i = 0; i < arrayDepth; i++)
	{
		commandHead[i].channel = arrayCurr->commHandle->channel;
		commandHead[i].command = arrayCurr->commHandle->command;
		commandHead[i].buff[0] = arrayCurr->commHandle->buff[0];
		commandHead[i].buff[1] = arrayCurr->commHandle->buff[1];

		d = commandHead[i].buff[0];
		d = commandHead[i].buff[1];
		arrayPrev = arrayCurr;
		arrayCurr = arrayCurr->next;

		free(arrayPrev->commHandle);
		free(arrayPrev);
	}
}

int MIDIObj::StripChannelNotes(int channelNumber, unsigned long* outBuffer, int outLen)
{
	int i;
	int depth = 0;

	int noteOffset[7] = {9, 11, 0, 2, 4, 5, 7}; //A, B, C, D, E, F, G
	const double A4Ref = 440;
    const double RefOctave = 4;
    const double RefOffset = 60 + 9; //(MIDI_C4 + 9 half steps = A4

	for(i = 0; i < numCommands; i++)
	{
		if(depth > outLen)
			return -1;

		if( (commandHead[i].command == 1) && (commandHead[i].channel == channelNumber) )
		{ 
			unsigned char d = commandHead[i].buff[1];
			outBuffer[depth] = pow(2, (d - RefOffset) / 12) * A4Ref * 100;
			depth++;
		}

	}

	if (depth < outLen)
		outBuffer[depth] = 0;

	return 0;
}